﻿using Com.Lancetinc.Evs.Config;
using Com.Lancetinc.Evs.Controls;
using Com.Lancetinc.Evs.Helpers;
using Com.Lancetinc.Evs.Reports.RadPdf;
using Com.Lancetinc.Evs.Resources;
using Com.Lancetinc.Evs.Services;
using System.Collections.Generic;
using System.IO;
using System.Printing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Xps.Packaging;
using Com.Lancetinc.Evs.Data.Domain;
using Telerik.Windows.Documents.Fixed.FormatProviders.Pdf;
using Telerik.Windows.Documents.Fixed.FormatProviders.Pdf.Export;
using Telerik.Windows.Documents.Fixed.Model;
using Telerik.Windows.Documents.Fixed.Model.Editing;

namespace Com.Lancetinc.Evs.Reports;

public static class ReportHelper
{
    public const string BannerFileName = "banner.png";

    private static string GetAssetsDir(bool withLang)
    {
        var exeDir = DirUtil.ExeDirectory;
        var assetsDir = Path.Combine(exeDir, "Assets");
        if (withLang)
        {
            assetsDir = Path.Combine(assetsDir, I18nHelper.Default.Lang);
        }

        return assetsDir;
    }

    public static string GetBannerFullPath()
    {
        var assetsDir = GetAssetsDir(true);
        return Path.Combine(assetsDir, BannerFileName);
    }

    public static ImageSource LoadBannerImage()
    {
        if (!AppConfigs.ReportConfig.UseBanner)
            return null;

        var bannerPath = GetBannerFullPath();
        if (!File.Exists(bannerPath))
        {
            return null;
        }

        return ResourceHelper.GetImageFromFile(bannerPath, false);
    }

    public static string SaveBanner(string saveBannerFile)
    {
        var bannerPath = GetBannerFullPath();
        FileUtil.EnsureDirectoryExists(bannerPath);
        File.Copy(saveBannerFile, bannerPath, true);
        return BannerFileName;
    }

    public static string GetReportTitle()
    {
        return AppConfigs.ReportConfig.ReportTitle;
    }

    public static string GetReportSubTitle()
    {
        return AppConfigs.ReportConfig.ReportSubTitle;
    }

    public static IEnumerable<ReportTerminology> LoadReportTerminologies()
    {
        var terms = AppConfigs.ReportConfig.Terms;
        if (string.IsNullOrEmpty(terms))
            yield break;

        var groups = new List<List<string>>();
        List<string> termList = null;
        foreach (var line in terms.SplitNewLine(true))
        {
            var l = line.Trim();
            if (string.IsNullOrEmpty(l))
                continue;

            if (l.StartsWith(ReportTerminology.GroupPrefix))
            {
                termList = new List<string>();
                groups.Add(termList);
            }
            termList?.Add(l);
        }

        foreach (var tl in groups)
        {
            yield return ReportTerminology.Parse(tl.ToArray());
        }
    }

    public static IEnumerable<string> SplitReportImages(string reportImages)
    {
        return reportImages.SplitNewLine(true);
    }

    /// <summary>
    /// 取得报告图片全路径，过滤不存在的图片
    /// </summary>
    /// <param name="seqNo"></param>
    /// <param name="reportImages"></param>
    /// <returns></returns>
    public static IEnumerable<string> ParseReportImages(string seqNo, string reportImages)
    {
        if (string.IsNullOrEmpty(reportImages))
            yield break;

        var pictureRoot = ExperManager.Default.GetPictureDir(seqNo);
        foreach (var img in SplitReportImages(reportImages))
        {
            var file = Path.Combine(pictureRoot, img);
            if (!File.Exists(file))
                continue;

            yield return file;
        }
    }

    private static Exper _cachedExper;
    /// <summary>
    /// 取得报告预览用的实验测试数据
    /// </summary>
    /// <returns></returns>
    public static Exper GetReportTemplateData()
    {
        if (_cachedExper != null)
            return _cachedExper;

        var resourceFile = $"ReportTemplateData_{I18nHelper.Default.Lang}.json";
        using var stream = typeof(ReportTemplateSettingPreviewUserControl).Assembly.GetManifestResourceStreamEx(resourceFile);
        var json = StreamUtil.StreamToString(stream);
        _cachedExper = JsonUtil.Parse<Exper>(json);
        return _cachedExper;
    }

    private static readonly object _illustrationImgLock = new();
    /// <summary>
    /// 取得模版预览时用的插图
    /// </summary>
    /// <returns></returns>
    public static string GetIllustrationForTemplatePreview()
    {
        lock (_illustrationImgLock)
        {
            var assetsDir = GetAssetsDir(true);
            var illustrationFileName = "illustration.png";
            var illustrationFilePath = Path.Combine(assetsDir, illustrationFileName);
            if (File.Exists(illustrationFilePath))
                return illustrationFilePath;

            using var stream = typeof(ReportHelper).Assembly.GetManifestResourceStreamEx(illustrationFileName);
            FileUtil.EnsureDirectoryExists(illustrationFilePath);
            using var fileStream = File.Create(illustrationFilePath);
            stream.CopyTo(fileStream);
            return illustrationFilePath;
        }
    }

    public static void AddPage(this FixedDocument doc, FixedPage page)
    {
        var pageContent = new PageContent();
        ((IAddChild)pageContent).AddChild(page);
        doc.Pages.Add(pageContent);
    }

    public static bool Print(this FixedDocument doc, string description, bool showDialog, string printerName = null)
    {
        var pd = new PrintDialog();
        if (!string.IsNullOrEmpty(printerName))
        {
            pd.PrintQueue = new PrintQueue(new PrintServer(), printerName);
        }

        if (showDialog)
        {
            if (pd.ShowDialog() != true)
                return false;
        }
        pd.PrintDocument(doc.DocumentPaginator, description);
        return true;
    }

    public static FixedDocument CreateDocument(FrameworkElement ctrl)
    {
        var doc = new FixedDocument();
        var fixedPage = new FixedPage
        {
            Margin = new Thickness(0),
            IsHitTestVisible = false
        };

        ctrl.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        ctrl.Arrange(new Rect(new Point(0, 0), ctrl.DesiredSize));

        var paperWidth = ctrl.Width;
        var paperHeight = ctrl.Height;
        fixedPage.Width = paperWidth;
        fixedPage.Height = paperHeight;
        doc.DocumentPaginator.PageSize = new Size(paperWidth, paperHeight);
        fixedPage.Children.Add(ctrl);

        doc.AddPage(fixedPage);
        return doc;
    }

    #region PrintToPdf

    /// <summary>
    /// 把WPF控件打印到PDF
    /// </summary>
    /// <param name="element"></param>
    /// <param name="pageSize"></param>
    /// <param name="pdfFile"></param>
    public static void PrintToPdf(FrameworkElement element, Size pageSize, string pdfFile)
    {
        PrepareForExport(element, pageSize);

        var document = CreateRadDocument(element, pageSize);
        var provider = new PdfFormatProvider();
        provider.ExportSettings.ImageQuality = ImageQuality.High;
        using var output = File.Create(pdfFile);
        provider.Export(document, output);
    }

    private static void PrepareForExport(FrameworkElement element, Size pageSize)
    {
        if (element.ActualWidth > 0 || element.ActualHeight > 0)
        {
            return;
        }
        double width = element.Width > 0 ? element.Width : pageSize.Width;
        double height = element.Height > 0 ? element.Height : pageSize.Height;
        element.Measure(Size.Empty);
        element.Measure(new Size(width, height));
        element.Arrange(new Rect(0, 0, width, height));
        element.UpdateLayout();
    }

    private static RadFixedDocument CreateRadDocument(FrameworkElement element, Size pageSize)
    {
        var document = new RadFixedDocument();
        var page = CreateRadPage(element, pageSize);
        document.Pages.Add(page);
        return document;
    }

    private static RadFixedPage CreateRadPage(FrameworkElement element, Size pageSize)
    {
        var page = new RadFixedPage();
        page.Size = pageSize;
        var editor = new FixedContentEditor(page, Telerik.Windows.Documents.Fixed.Model.Data.MatrixPosition.Default);
        ExportHelper.ExportToPdf(element, editor);

        return page;
    }

    #endregion

    /// <summary>
    /// 打印到XPS文件
    /// </summary>
    /// <param name="element"></param>
    /// <param name="pageSize"></param>
    /// <param name="xpsFile"></param>
    public static void PrintToXps(FrameworkElement element, Size pageSize, string xpsFile)
    {
        var doc = CreateDocument(element);
        using var xpsDoc = new XpsDocument(xpsFile, FileAccess.Write);
        var writer = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
        var dp = doc.DocumentPaginator;
        dp.PageSize = pageSize;
        writer.Write(dp);
    }
}